package com.lhb.aicodemother.ai.core;

import cn.hutool.json.JSONUtil;
import com.lhb.aicodemother.ai.AiCodeGeneratorService;
import com.lhb.aicodemother.ai.AiCodeGeneratorServiceFactory;
import com.lhb.aicodemother.ai.core.parser.CodeParserExecutor;
import com.lhb.aicodemother.ai.core.saver.CodeFileSaverExecutor;
import com.lhb.aicodemother.ai.model.HtmlCodeResult;
import com.lhb.aicodemother.ai.model.MultiFileCodeResult;
import com.lhb.aicodemother.ai.model.enums.CodeGenTypeEnum;
import com.lhb.aicodemother.ai.model.message.AiResponseMessage;
import com.lhb.aicodemother.ai.model.message.ToolExecutionMessage;
import com.lhb.aicodemother.ai.model.message.ToolRequestMessage;
import com.lhb.aicodemother.exception.BusinessException;
import com.lhb.aicodemother.exception.ErrorCode;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;

/*
* AI代码生成外观类，组合生成和保存功能
* */
@Slf4j
@Service
public class AiCodeGeneratorFacade {

    @Resource
    private AiCodeGeneratorServiceFactory aiCodeGeneratorServiceFactory;

    /*
    * 统一入口，根据类型生成并保存代码
    * */
    public File generateAndSaveCode(String userMessage, CodeGenTypeEnum codeGenTypeEnum,Long appId){
        if(codeGenTypeEnum == null){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"生成类型为空");
        }
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAICodeGeneratorService(appId,codeGenTypeEnum);
        return switch (codeGenTypeEnum){
            case HTML -> {
                HtmlCodeResult htmlCodeResult = aiCodeGeneratorService.generateHtmlCode(userMessage);
                yield CodeFileSaverExecutor.executeSaver(htmlCodeResult,codeGenTypeEnum,appId);
            }
            case MULTI_FILE -> {
                MultiFileCodeResult multiFileCodeResult = aiCodeGeneratorService.generateMultiFileCode(userMessage);
                yield CodeFileSaverExecutor.executeSaver(multiFileCodeResult,codeGenTypeEnum,appId);
            }
            default -> {
                String errorMessage = "不支持的生成类型" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,errorMessage);
            }
        };
    }

    /*
    * 统一入口，根据类型生成并保存代码（流式）
    * */
    public Flux<String> generateAndSaveCodeStream(String userMessage, CodeGenTypeEnum codeGenTypeEnum,Long appId){
        if(codeGenTypeEnum == null){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"生成类型为空");
        }
        AiCodeGeneratorService aiCodeGeneratorService = aiCodeGeneratorServiceFactory.getAICodeGeneratorService(appId,codeGenTypeEnum);
        return switch (codeGenTypeEnum){
            case HTML -> {
                Flux<String> flux = aiCodeGeneratorService.generateHtmlCodeStream(userMessage);
                yield processCodeStream(flux,codeGenTypeEnum,appId);
            }
            case MULTI_FILE -> {
                Flux<String> flux = aiCodeGeneratorService.generateMultiFileCodeStream(userMessage);
                yield processCodeStream(flux,codeGenTypeEnum,appId);
            }
            case VUE_PROJECT -> {
                dev.langchain4j.service.TokenStream tokenStream = aiCodeGeneratorService.generateVueProjectCodeStream(appId, userMessage);
                Flux<String> flux = processTokenStream(tokenStream);
                yield processCodeStream(flux,CodeGenTypeEnum.MULTI_FILE,appId);
            }
            default -> {
                String errorMessage = "不支持的生成类型" + codeGenTypeEnum.getValue();
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,errorMessage);
            }
        };
    }

    /*
    * 通用流式代码处理方法
    *
    * */
    private Flux<String> processCodeStream(Flux<String> result,CodeGenTypeEnum codeGenTypeEnum,Long appId){
        // 字符串拼接
        StringBuilder stringBuilder = new StringBuilder();
        return result
                .doOnNext(chunk -> {
                    stringBuilder.append(chunk);
                })
                .doOnComplete(()->{
                    try{
                        String completeCode = stringBuilder.toString();
                        // 使用执行器解析代码
                        Object parserResult = CodeParserExecutor.executeParser(completeCode, codeGenTypeEnum);
                        // 使用执行器保存代码
                        File saver = CodeFileSaverExecutor.executeSaver(parserResult, codeGenTypeEnum,appId);
                        log.info("保存成功，路径为：{}",saver.getAbsolutePath());
                    }catch (Exception e){
                        log.error("保存失败：{}",e.getMessage());
                    }
                });
    }

    /*
    * 将TokenStream转换为Flux<String>，并传递工具调用信息
    * */
    private Flux<String> processTokenStream(TokenStream tokenStream){
        return Flux.create(sink -> {
            tokenStream.onPartialResponse((String partialResponse) -> {
                AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
                sink.next(JSONUtil.toJsonStr(aiResponseMessage));
            })
                    .onPartialToolExecutionRequest((index,toolExecutionRequest) -> {
                        ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
                        sink.next(JSONUtil.toJsonStr(toolRequestMessage));
                    })
                    .onToolExecuted((ToolExecution toolExecution) -> {
                        ToolExecutionMessage toolExecutionMessage = new ToolExecutionMessage(toolExecution);
                        sink.next(JSONUtil.toJsonStr(toolExecutionMessage));
                    })
                    .onCompleteResponse((ChatResponse response) -> {
                        sink.complete();
                    })
                    .onError((Throwable throwable) -> {
                        throwable.printStackTrace();
                        sink.error(throwable);
                    })
                    .start();
        });
    }
}
